
/*backtest
start: 2021-07-01 00:00:00
end: 2021-07-27 23:59:00
period: 30m
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

// 首次买入
let FIRST_BUY = _G("FIRST_BUY") || true;
// 已存在买涨订单
let MANY_BUYING = _G("MANY_BUYING") || false;
// 已存在做空订单
let SHORT_BUYING = _G("SHORT_BUYING") || false;

// 买涨订单创建时间
let MANY_BUY_TIME = _G("MANY_BUY_TIME");
// 做空订单创建时间
let SHORT_BUY_TIME = _G("SHORT_BUY_TIME");
// 买涨空仓时间
let MANY_EMPTY_STEP_TIME = _G("MANY_EMPTY_STEP_TIME");
// 做空空仓时间
let SHORT_EMPTY_STEP_TIME = _G("SHORT_EMPTY_STEP_TIME");
// 校验空仓时间
let CHECK_TIME = _G("CHECK_TIME");

// let QUANTITY = [40, 50, 60, 80, 120, 200, 400];
let QUANTITY = [0.7, 1, 1.5, 2.8, 4, 6, 8];
let DOUBLE_THROW_RATIOS = [1.5, 1.6, 1.75, 1.8, 2.0, 2.8, 3.5];
// 下次购买价格(多仓)
let MANY_NEXT_BUY_PRICE = Number(_G("MANY_NEXT_BUY_PRICE") || 0);
// 下次购买价格(空仓)
let SHORT_NEXT_BUY_PRICE = Number(_G("SHORT_NEXT_BUY_PRICE") || 0);
// 当前仓位(多仓)
let MANY_STEP = Number(_G("MANY_STEP") || 0);
// 当前仓位(空仓)
let SHORT_STEP = Number(_G("SHORT_STEP") || 0);
// 止盈比率
let PROFIT_RATIO = 1;
// 补仓比率
let DOUBLE_THROW_RATIO = 1.5;
// 卖出后下次购买金额下浮比率
let BUY_PRICE_RATIO = 1;
// 交易订单列表(多仓)
let MANY_ORDER_LIST = _G("MANY_ORDER_LIST") || [];
// 交易订单列表(空仓)
let SHORT_ORDER_LIST = _G("SHORT_ORDER_LIST") || [];

function resetSaveData() {
    _G("FIRST_BUY", null)
    _G("MANY_BUYING", null)
    _G("SHORT_BUYING", null)
    _G("MANY_BUY_TIME", null)
    _G("SHORT_BUY_TIME", null)
    _G("MANY_EMPTY_STEP_TIME", null)
    _G("SHORT_EMPTY_STEP_TIME", null)
    _G("CHECK_TIME", null)

    _G("MANY_NEXT_BUY_PRICE", null)
    _G("SHORT_NEXT_BUY_PRICE", null)

    _G("MANY_STEP", null)
    _G("SHORT_STEP", null)

    _G("MANY_ORDER_LIST", null)
    _G("SHORT_ORDER_LIST", null)
}

function LogSaveData() {
    Log("FIRST_BUY", _G("FIRST_BUY"))
    Log("MANY_BUYING", _G("MANY_BUYING"))

    Log("SHORT_BUYING", _G("SHORT_BUYING"))
    Log("MANY_BUY_TIME", _G("MANY_BUY_TIME"))

    Log("SHORT_EMPTY_STEP_TIME", _G("SHORT_EMPTY_STEP_TIME"))
    Log("CHECK_TIME", _G("CHECK_TIME"))

    Log("MANY_NEXT_BUY_PRICE", _G("MANY_NEXT_BUY_PRICE"))
    Log("SHORT_NEXT_BUY_PRICE", _G("SHORT_NEXT_BUY_PRICE"))

    Log("MANY_STEP", _G("SHORT_STEP"))
    Log("SHORT_STEP", _G("SHORT_STEP"))

    Log("MANY_ORDER_LIST", _G("MANY_ORDER_LIST"))
    Log("SHORT_ORDER_LIST", _G("SHORT_ORDER_LIST"))
}

LogSaveData()


var accountTime = Date.now()

var position = [] //_G("ORDER_POSITION") || []

function updateTables() {
    let shouyiTable = tableForJiaoYiInfo(position)
    let pingduoTable = tableForPingDuo("做多", MANY_ORDER_LIST)
    let pingkongTable = tableForPingKong("做空", SHORT_ORDER_LIST)
    LogStatus("`" + JSON.stringify([shouyiTable, pingduoTable, pingkongTable]) + "`")
    // LogStatus("`" + JSON.stringify([pingduoTable, pingkongTable]) + "`")
}

function tableForJiaoYiInfo(array) {
    var shouyiTable = {
        type: "table",
        title: "交易对信息",
        cols: ['币种', '方向', '个数', '保证金', '实现盈亏'],
        rows: []
    }
    array.forEach(item => {
        shouyiTable.rows.push([item.Info.symbol,
            item.Info.positionSide,
            item.Info.positionAmt,
            item.Margin,
            item.Info.unRealizedProfit])
    })
    //table.rows.push([array, button2, button3])
    // LogStatus("`" + JSON.stringify(table) + "`")
    return shouyiTable
}

function tableForProfit() {
    var account = exchange.GetAccount()
    var data = account.Info.assets[1]
    var marginBalance = data.marginBalance
    var unrealizedProfit = Number(data.unrealizedProfit)
    LogProfit(marginBalance - 500, '&')

    Log("当前未实现盈亏：", _N(unrealizedProfit, 2) + 'U');
    position = exchange.GetPosition()
    // tableForJiaoYiInfo(position)
    updateTables()
}

// {"buyPrice":0.3354,"sellPrice":0.338754,"quantity":40,"isSell":false}
function tableForPingDuo(tpye, array) {
    var pingduoTable = {
        type: "table",
        title: "待平多",
        cols: ['购买价格', '出售价格', '个数', '交易时间'],
        rows: []
    }
    array.forEach(item => {
        pingduoTable.rows.push([item.buyPrice,
            item.sellPrice,
            item.quantity,
            item.updateTime ? _D(Number(item.updateTime)) : '暂无'])
    })
    //table.rows.push([array, button2, button3])
    // LogStatus("`" + JSON.stringify(table) + "`")
    return pingduoTable
}

// {"buyPrice":0.3354,"sellPrice":0.338754,"quantity":40,"isSell":false}
function tableForPingKong(tpye, array) {
    var pingkongTable = {
        type: "table",
        title: "待平空",
        cols: ['购买价格', '出售价格', '个数', '交易时间'],
        rows: []
    }
    array.forEach(item => {
        pingkongTable.rows.push([item.buyPrice,
            item.sellPrice,
            item.quantity,
            item.updateTime ? _D(Number(item.updateTime)) : '暂无'])
    })
    //table.rows.push([array, button2, button3])
    // LogStatus("`" + JSON.stringify(table) + "`")
    return pingkongTable
}

function getManyQuantity() {
    if (MANY_STEP < QUANTITY.length) {
        return QUANTITY[MANY_STEP]
    }
    return QUANTITY[0]
}

function getShortQuantity() {
    if (SHORT_STEP < QUANTITY.length) {
        return QUANTITY[SHORT_STEP]
    }
    return QUANTITY[0]
}


function getManyRatio() {
    if (MANY_STEP < DOUBLE_THROW_RATIOS.length) {
        return DOUBLE_THROW_RATIOS[MANY_STEP]
    }
    return DOUBLE_THROW_RATIOS[0]
}

function getShortRatio() {
    if (SHORT_STEP < DOUBLE_THROW_RATIOS.length) {
        return DOUBLE_THROW_RATIOS[SHORT_STEP]
    }
    return DOUBLE_THROW_RATIOS[0]
}


function firstManyBuy(ticker) {
    if (MANY_BUYING) {
        return
    }
    exchange.SetDirection("buy")
    let orderId = exchange.Buy(-1, getManyQuantity())
    if (!orderId) {
        return
    }
    tableForProfit()
    MANY_BUYING = true
    while (true) {
        exchange.SetDirection("buy")
        let order = exchange.GetOrder(orderId)
        if (null === order) {
            continue
        }
        if (1 === order.Status || 2 === order.Status) {
            MANY_NEXT_BUY_PRICE = order.AvgPrice * ((100 - getManyRatio()) / 100)
            MANY_STEP = MANY_STEP + 1
            MANY_BUYING = false
            MANY_EMPTY_STEP_TIME = null
            let sellPrice = order.AvgPrice * ((100 + PROFIT_RATIO) / 100)
            MANY_ORDER_LIST.push({
                buyPrice: order.AvgPrice,
                sellPrice: sellPrice,
                quantity: order.Amount,
                updateTime: order.Info.updateTime,
                isSell: false,
            })
            updateTables()
            // tableForProfit()
            //
            break
        }
    }
    _G("MANY_NEXT_BUY_PRICE", MANY_NEXT_BUY_PRICE)
    _G("MANY_STEP", MANY_STEP)
    _G("MANY_BUYING", MANY_BUYING)
    _G("MANY_EMPTY_STEP_TIME", MANY_EMPTY_STEP_TIME)
    _G("MANY_ORDER_LIST", MANY_ORDER_LIST)
}

function firstShortBuy(ticker) {
    if (SHORT_BUYING) {
        return
    }
    exchange.SetDirection("sell")
    let orderId = exchange.Sell(-1, getShortQuantity())
    if (!orderId) {
        return
    }
    tableForProfit()
    SHORT_BUYING = true
    while (true) {
        let order = exchange.GetOrder(orderId)

        if (null === order) {
            continue
        }
        if (1 === order.Status || 2 === order.Status) {
            SHORT_NEXT_BUY_PRICE = order.AvgPrice * ((100 + getShortRatio()) / 100)
            SHORT_STEP = SHORT_STEP + 1
            SHORT_BUYING = false
            SHORT_EMPTY_STEP_TIME = null
            let sellPrice = order.AvgPrice * ((100 - PROFIT_RATIO) / 100)
            SHORT_ORDER_LIST.push({
                buyPrice: order.AvgPrice,
                sellPrice: sellPrice,
                quantity: order.Amount,
                updateTime: order.Info.updateTime,
                isSell: false,
            })
            updateTables()
            // tableForProfit()
            break
        }
    }
    _G("SHORT_NEXT_BUY_PRICE", SHORT_NEXT_BUY_PRICE)
    _G("SHORT_STEP", SHORT_STEP)
    _G("SHORT_BUYING", SHORT_BUYING)
    _G("SHORT_EMPTY_STEP_TIME", SHORT_EMPTY_STEP_TIME)
    _G("SHORT_ORDER_LIST", SHORT_ORDER_LIST)
}

function manyBuy(ticker) {
    if (MANY_BUYING) {
        return
    }
    if (ticker.Last > MANY_NEXT_BUY_PRICE) {
        return
    }
    exchange.SetDirection("buy")
    let orderId = exchange.Buy(-1, getManyQuantity())
    if (!orderId) {
        return
    }
    tableForProfit()
    MANY_BUYING = true
    MANY_BUY_TIME = Unix()
    while (true) {
        let now = Unix()
        let order = exchange.GetOrder(orderId)
        let expire = MANY_BUY_TIME + (60 * 30)
        if (null === order) {
            continue
        }
        // 买入成功处理
        if (1 === order.Status || 2 === order.Status) {
            MANY_NEXT_BUY_PRICE = order.AvgPrice * ((100 - getManyRatio()) / 100)
            Log('做多 当前价格: ' + order.AvgPrice + '下次价格：' + MANY_NEXT_BUY_PRICE + '比例：' + getManyRatio())
            MANY_STEP = MANY_STEP + 1
            MANY_BUYING = false
            MANY_EMPTY_STEP_TIME = null
            let sellPrice = order.AvgPrice * ((100 + PROFIT_RATIO) / 100)
            MANY_ORDER_LIST.push({
                buyPrice: order.AvgPrice,
                sellPrice: sellPrice,
                quantity: order.Amount,
                updateTime: order.Info.updateTime,
                isSell: false,
            })
            // tableForProfit()
            updateTables()
            //if (SHORT_ORDER_COUNT === 0) {
            //    // 开始加
            //    MANY_ORDER_COUNT += 1
            //    MANY_ORDER_QUANTITY += order.Amount
            //
            //    if (MANY_ORDER_COUNT > 3) {
            //        // 对冲
            //        exchange.SetDirection("sell")
            //        let id = exchange.Sell(ticker.Last, MANY_ORDER_QUANTITY)
            //
            //        MANY_ORDER_COUNT = 0
            //        MANY_ORDER_QUANTITY = 0
            //    }
            //}else {
            //    SHORT_ORDER_COUNT = 0
            //    SHORT_ORDER_QUANTITY = 0
            //}
            break
        }
        // 买入超时处理
        if (now >= expire) {
            exchange.CancelOrder(orderId)
            MANY_BUYING = false
            MANY_BUY_TIME = null
            MANY_NEXT_BUY_PRICE = ticker.Last * ((100 - DOUBLE_THROW_RATIO) / 100)
            return
        }
    }
    _G("MANY_BUY_TIME", MANY_BUY_TIME)
    _G("MANY_NEXT_BUY_PRICE", MANY_NEXT_BUY_PRICE)
    _G("MANY_STEP", MANY_STEP)
    _G("MANY_BUYING", MANY_BUYING)
    _G("MANY_EMPTY_STEP_TIME", MANY_EMPTY_STEP_TIME)
    _G("MANY_ORDER_LIST", MANY_ORDER_LIST)
}

function shortBuy(ticker) {
    if (SHORT_BUYING) {
        return
    }
    if (ticker.Last < SHORT_NEXT_BUY_PRICE) {
        return
    }
    exchange.SetDirection("sell")
    let orderId = exchange.Sell(-1, getShortQuantity())
    if (!orderId) {
        return
    }
    tableForProfit()
    SHORT_BUYING = true
    SHORT_BUY_TIME = Unix()
    while (true) {
        let now = Unix()
        let expire = SHORT_BUY_TIME + (60 * 30)
        let order = exchange.GetOrder(orderId)
        if (null === order) {
            continue
        }
        // 买入成功处理
        if (1 === order.Status || 2 === order.Status) {
            SHORT_NEXT_BUY_PRICE = order.AvgPrice * ((100 + getShortRatio()) / 100)
            Log('做空 当前价格: ' + order.AvgPrice + '下次价格：' + SHORT_NEXT_BUY_PRICE + '比例：' + getShortRatio())
            SHORT_STEP = SHORT_STEP + 1
            SHORT_BUYING = false
            SHORT_EMPTY_STEP_TIME = null
            let sellPrice = order.AvgPrice * ((100 - PROFIT_RATIO) / 100)
            SHORT_ORDER_LIST.push({
                buyPrice: order.AvgPrice,
                sellPrice: sellPrice,
                quantity: order.Amount,
                updateTime: order.Info.updateTime,
                isSell: false,
            })
            // tableForProfit()
            updateTables()
            //if (MANY_ORDER_COUNT === 0) {
            //    // 开始加
            //    SHORT_ORDER_COUNT += 1
            //    SHORT_ORDER_QUANTITY += order.Amount

            //    if (SHORT_ORDER_COUNT > 3) {
            //        // 对冲
            //        exchange.SetDirection("buy")
            //        let id = exchange.Buy(ticker.Last, SHORT_ORDER_QUANTITY)
            //
            //        SHORT_ORDER_COUNT = 0
            //        SHORT_ORDER_QUANTITY = 0
            //    }
            //}else {
            //    MANY_ORDER_COUNT = 0
            //    MANY_ORDER_QUANTITY = 0
            //}
            break
        }
        // 买入超时处理
        if (now >= expire) {
            exchange.CancelOrder(orderId)
            SHORT_BUYING = false
            SHORT_BUY_TIME = null
            SHORT_NEXT_BUY_PRICE = ticker.Last * ((100 + DOUBLE_THROW_RATIO) / 100)
            return
        }
    }
    _G("SHORT_BUY_TIME", SHORT_BUY_TIME)
    _G("SHORT_NEXT_BUY_PRICE", SHORT_NEXT_BUY_PRICE)
    _G("SHORT_STEP", SHORT_STEP)
    _G("SHORT_BUYING", SHORT_BUYING)
    _G("SHORT_EMPTY_STEP_TIME", SHORT_EMPTY_STEP_TIME)
    _G("SHORT_ORDER_LIST", SHORT_ORDER_LIST)
}


function manySell(ticker) {
    // 遍历卖出订单
    for (let item of MANY_ORDER_LIST) {
        if (item.isSell) {
            continue
        }
        if (ticker.Last >= item.sellPrice) {
            item.isSell = true;
            exchange.SetDirection("closebuy")
            let orderId = exchange.Sell(-1, item.quantity)
            if (!orderId) {
                return
            }
            tableForProfit()
            while (true) {
                let order = exchange.GetOrder(orderId)
                if (null === order) {
                    continue
                }
                if (1 === order.Status || 2 === order.Status) {
                    MANY_STEP = MANY_STEP - 1
                    MANY_NEXT_BUY_PRICE = ticker.Last * ((100 - getManyRatio()) / 100)
                    Log('平多 当前价格: ' + order.AvgPrice + '下次做多价格：' + MANY_NEXT_BUY_PRICE + '比例：' + getManyRatio())
                    if (0 === MANY_STEP) {
                        MANY_EMPTY_STEP_TIME = Unix()
                    }
                    break
                }
            }
        }
    }
    MANY_ORDER_LIST = MANY_ORDER_LIST.filter(item => !item.isSell)
    // tableForProfit()
    updateTables()
    _G("MANY_ORDER_LIST", MANY_ORDER_LIST)
    _G("MANY_NEXT_BUY_PRICE", MANY_NEXT_BUY_PRICE)
    _G("MANY_STEP", MANY_STEP)
    _G("MANY_EMPTY_STEP_TIME", MANY_EMPTY_STEP_TIME)
}

function shortSell(ticker) {
    // 遍历卖出订单
    for (let item of SHORT_ORDER_LIST) {
        if (item.isSell) {
            continue
        }
        if (ticker.Last <= item.sellPrice) {
            item.isSell = true;
            exchange.SetDirection("closesell")
            let orderId = exchange.Buy(-1, item.quantity)
            if (!orderId) {
                return
            }
            tableForProfit()
            while (true) {
                let order = exchange.GetOrder(orderId)
                if (null === order) {
                    continue
                }
                if (1 === order.Status || 2 === order.Status) {
                    SHORT_STEP = SHORT_STEP - 1
                    SHORT_NEXT_BUY_PRICE = ticker.Last * ((100 + getShortRatio()) / 100)
                    Log('平空 当前价格: ' + order.AvgPrice + '下次做空价格：' + SHORT_NEXT_BUY_PRICE + '比例：' + getShortRatio())
                    if (0 === SHORT_STEP) {
                        SHORT_EMPTY_STEP_TIME = Unix()
                    }
                    break
                }
            }
        }
    }
    SHORT_ORDER_LIST = SHORT_ORDER_LIST.filter(item => !item.isSell)
    updateTables()
    // tableForProfit()
    _G("SHORT_ORDER_LIST", SHORT_ORDER_LIST)
    _G("SHORT_NEXT_BUY_PRICE", SHORT_NEXT_BUY_PRICE)
    _G("SHORT_STEP", SHORT_STEP)
    _G("SHORT_EMPTY_STEP_TIME", SHORT_EMPTY_STEP_TIME)
}

function check(ticker) {
    let now = Unix()
    if (null !== CHECK_TIME) {
        let expire = CHECK_TIME + (60 * 10)
        if (now < expire) {
            return
        }
    }
    CHECK_TIME = now

    if (null !== MANY_EMPTY_STEP_TIME) {
        let expire = MANY_EMPTY_STEP_TIME + (60 * 30)
        Log("check-MANY:", expire)
        if (now >= expire) {
            MANY_NEXT_BUY_PRICE = ticker.Last * ((100 - getManyRatio()) / 100)
            Log('没有买涨持仓, 调整买入价: ' + MANY_NEXT_BUY_PRICE)
        }
    }

    if (null !== SHORT_EMPTY_STEP_TIME) {
        let expire = SHORT_EMPTY_STEP_TIME + (60 * 30)
        Log("check-SHORT:", expire)
        if (now >= expire) {
            SHORT_NEXT_BUY_PRICE = ticker.Last * ((100 + getShortRatio()) / 100)
            Log('没有做空持仓, 调整买入价: ' + SHORT_NEXT_BUY_PRICE)
        }
    }
    _G("MANY_NEXT_BUY_PRICE", MANY_NEXT_BUY_PRICE)
    _G("SHORT_NEXT_BUY_PRICE", SHORT_NEXT_BUY_PRICE)
}

function onTick(ticker) {
    // 在这里写策略逻辑，将会不断调用，例如打印行情信息
    // let ticker = exchange.GetTicker()
    if (!ticker) {
        return
    }
    if (FIRST_BUY) {
        // 首次做多购买
        firstManyBuy(ticker)
        // 首次做空购买
        firstShortBuy(ticker)
        FIRST_BUY = false
        return
    }
    // 做多买入
    manyBuy(ticker)
    // 做空买入
    shortBuy(ticker)
    // 做多卖出
    manySell(ticker)
    // 做空卖出
    shortSell(ticker)
    // 空仓检测
    check(ticker)

    if(Date.now() - accountTime > 2 * 60 * 1000){//这里即限制了2分钟内只获取账户一次
        accountTime = Date.now()
        tableForProfit()
    }
}

function main() {
    // 开合约
    exchange.SetContractType("swap")

    Log("开始链接。。。")
    var client = Dial("wss://fstream.binance.com/ws/linkusdt@markPrice@1s")
    if (!client) {
        Log("连接失败, 程序退出")
        return
    }
    Sleep(5 * 1000)
    while(true){
        var buf = client.read(-2)
        if (!buf) {
            //Log("not buf")
            continue
        }

        var obj = JSON.parse(buf)
        try {
            onTick({Last: _N(Number(obj.p), 4)})
        } catch(err) {
            Log("error：", err)
        }
        Sleep(5 * 1000)
    }
    client.close()
}


// function onTick() {
//   // 在这里写策略逻辑，将会不断调用，例如打印行情信息
//   let ticker = exchange.GetTicker()
//   if (!ticker) {
//       return
//   }
//   if (FIRST_BUY) {
//       // 首次做多购买
//       firstManyBuy(ticker)
//       // 首次做空购买
//       firstShortBuy(ticker)
//       FIRST_BUY = false
//       return
//   }

//   // 做多买入
//   manyBuy(ticker)
//   // 做空买入
//   shortBuy(ticker)
//   // 做多卖出
//   manySell(ticker)
//   // 做空卖出
//   shortSell(ticker)
//   // 空仓检测
//   check(ticker)
// }

// function main() {
//    // 开合约
//    exchange.SetContractType("swap")

//    while(true){
//        onTick()
//        // Sleep函数主要用于数字货币策略的轮询频率控制，防止访问交易所API接口过于频繁
//        Sleep(60000)
//    }
// }
